//
// Created by hjie on 23-6-1.
//

#include "stun_client.h"

static int64_t tm = 0;
static int msg_count_index = 0;

StunClient::StunClient(LoopEvent *loop)
{
    m_loop_event = loop;
}

void StunClient::InitOption(StunClientOption &option)
{
    m_udp_client = new UdpClient(m_loop_event, option.m_server_ip, option.m_server_port);
    m_udp_client->SetOnMessage(std::bind(&StunClient::OnMessage, this, std::placeholders::_1));
    m_udp_client->StartClient();
    m_option = option;

    m_local_public_port = 0;
    m_media_client = std::make_shared<MediaProcessClient>(m_loop_event);

}

void StunClient::StartClient()
{
    SendBindRequest("");
}

void StunClient::OnMessage(UdpBufferPacket *packet)
{
    printf("recv data and length : %d\n", packet->buffer->Length());
    do
    {
        int use_length = 0;
        StunMessage message;
        ParseStatus status = message.ParseStunMessage(packet, use_length);
        if (status == EmParseFinishStatus || status == EmUnknownStatus)
        {
            packet->buffer->RemoveData(packet->buffer->Length());
            if (status == EmParseFinishStatus)
            {
                ProcessStunMessageByStatus(&message, packet);
                printf("parse finish !\n");
            }
            if (packet->buffer->Length() >= 16)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else
        {
            break;
        }
    } while (true);
}

void StunClient::ProcessStunMessageByStatus(StunMessage *message, UdpBufferPacket *packet)
{
    EmStunMsgType msg_type = message->GetMsgType();
    printf("msg type : %x\n", msg_type);
    if (msg_type == EmBindResponse)
    {
        ProcessHeartStatus(message, packet);
    }
}

void StunClient::ProcessHeartStatus(StunMessage *message, UdpBufferPacket *packet)
{
    AttrXorMappedAddress * addr = (AttrXorMappedAddress*)message->GetAttrById(EmXorMappedAddress).get();
    if (addr)
    {
        printf("xor local ip : %s and local port : %d\n", addr->LocalIp().c_str(), addr->LocalPort());
        if ((m_local_public_ip.compare(addr->LocalIp()) != 0 || m_local_public_port != addr->LocalPort()) && (m_local_public_port == 0))
        {
            m_local_public_ip   = addr->LocalIp();
            m_local_public_port = addr->LocalPort();
            m_media_client->ChangeIpPort(m_local_public_ip, m_local_public_port);
            if (!m_media_client->IsRunning())
            {
                m_media_client->ProcessMediaData();
            }
            SendBindRequest("");
        }
        else
        {
            std::string id = message->GetTransactionId();
            m_loop_event->PushAfterTask(15 * 1000, [this, id](){

                SendBindRequest(id);
            });
        }
    }
}

void StunClient::ProcessStunMessage(StunMessage *message, UdpBufferPacket *packet)
{
    uint16_t remote_port = be16toh(((struct sockaddr_in*)(packet->sai))->sin_port);
    char ip[32] = {0};
    inet_ntop(packet->sai->sa_family, &((struct sockaddr_in*)(packet->sai))->sin_addr, ip, sizeof(ip));
    printf("remote ip : %s and remote port : %d\n", ip, remote_port);

    std::map<MessageAttrType,std::shared_ptr<MessageAttr>> msg_attr = message->m_attr_map;
    for (auto & attr : msg_attr)
    {
        if (attr.first == EmXorMappedAddress)
        {
            AttrXorMappedAddress * xor_attr = (AttrXorMappedAddress*)attr.second.get();
            printf("local ip : %s and local port : %d\n", xor_attr->LocalIp().c_str(), xor_attr->LocalPort());
        }
    }
}

void StunClient::SendMessage()
{
    if (tm % 100 == 0)
    {
        std::string content = "hello world and index : " + std::to_string(msg_count_index++);
        m_udp_client->DoSendMsg((const uint8_t*)content.c_str(), content.length());
    }
    tm++;
    m_loop_event->PushTask([this](){

        SendMessage();
    });
}

void StunClient::SendBindRequest(const std::string & id)
{

}

void StunClient::SendAllocateRequest()
{
    
}


